﻿namespace Common.Protocol
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Text;

    using Common.Localization;

    /// <summary>
    /// Represents configuration of the channel and its values.
    /// </summary>
    public class Channel
    {
        // тип датчика - індекс для sensors_tb
        private byte sensorIndex;

        private int afterComma = 1;

        public static int SizeInBytes = 52;

        #region public properties

        /// <summary>
        /// Registration period. Channel is turned off if zero.
        /// </summary>
        public Int16 RegPeriod { get; private set; }

        public string Name { get; private set; }

        public string ValueUnits { get; private set; }

        public float Min { get; private set; }

        public float Max { get; private set; }

        public Color Color { get; private set; }

        public string Id { get; private set; }

        public List<UInt16> Values { get; private set; }

        public bool IsActive = true;

        public Block Block { get; private set; }

        public string Sensor { get; private set; }

        #endregion

        public Channel(Block block)
        {
            this.Block = block;
            this.Values = new List<ushort>();
        }

        /// <summary>
        /// Parses channel config from binary data.
        /// </summary>
        /// <param name="bytes">Array of bytes.</param>
        /// <param name="pos">Position from where to read.</param>
        /// <param name="id">Unique channel identifier.</param>
        /// <returns>True if channel is on and false otherwise.</returns>
        public bool ParseFromBinary(byte[] bytes, int pos, string id)
        {
            int position = pos + 4;
            this.RegPeriod = BitConverter.ToInt16(bytes, position);
            if (this.RegPeriod == 0)
            {
                return false;
            }
            else
            {
                this.Values = new List<UInt16>();
                this.Id = id;

                position = pos;
                int intColor = BitConverter.ToInt32(bytes, position);

                byte[] colorBytes = new byte[4];
                colorBytes = BitConverter.GetBytes(intColor);
                int r = BitConverter.ToInt32(new byte[] { colorBytes[2], 0, 0, 0 }, 0);
                int g = BitConverter.ToInt32(new byte[] { colorBytes[1], 0, 0, 0 }, 0);
                int b = BitConverter.ToInt32(new byte[] { colorBytes[0], 0, 0, 0 }, 0);

                this.Color = Color.FromArgb(r, g, b);

                position += 4;
                this.RegPeriod = BitConverter.ToInt16(bytes, position);

                position += 2;

                byte[] arr = new byte[26];

                for (int i = 0; i < 26; i++)
                {
                    arr[i] = bytes[i + position];
                }

                var a = Encoding.Convert(Encoding.GetEncoding("windows-1251"), Encoding.Unicode, arr);

                this.Name = Encoding.Unicode.GetString(a).TrimEnd(new char[] { '\0' });
                int index = this.Name.IndexOf('\0');
                if (index != -1)
                {
                    this.Name = this.Name.Substring(0, index);
                }

                position += 26;

                this.sensorIndex = bytes[position];
                this.Sensor = Common.Protocol.Sensor.GetSensorType("r" + this.sensorIndex);
                position++;

                int tempPos = position;
                arr = new byte[11];

                for (int i = 0; i < 11; i++)
                {
                    arr[i] = bytes[i + position];
                }

                a = Encoding.Convert(Encoding.GetEncoding("windows-1251"), Encoding.Unicode, arr);
                this.ValueUnits = Encoding.Unicode.GetString(a);
                index = this.ValueUnits.IndexOf('\0');
                if (index != -1)
                {
                    this.ValueUnits = this.ValueUnits.Substring(0, index);
                }

                position = tempPos + 11;
                this.Min = BitConverter.ToSingle(bytes, position);
                position += 4;
                this.Max = BitConverter.ToSingle(bytes, position);

                if (this.sensorIndex > 22)
                {
                    float fac = Math.Abs((this.Max - this.Min) * 0.001f);
                    this.afterComma = 0;
                    while (fac <= 0.999999)
                    {
                        this.afterComma++;
                        fac *= 10.0f;
                    }
                }

                return true;
            }
        }

        public static string GetIdFromNumber(int num)
        {
            if (num >= 32 || num < 0)
            {
                throw new ArgumentException("Wrong number!");
            }

            switch (num)
            {
                case 0:
                    return "A1";
                case 1:
                    return "A2";
                case 2:
                    return "A3";
                case 3:
                    return "A4";
                case 4:
                    return "B1";
                case 5:
                    return "B2";
                case 6:
                    return "B3";
                case 7:
                    return "B4";
                case 8:
                    return "C1";
                case 9:
                    return "C2";
                case 10:
                    return "C3";
                case 11:
                    return "C4";
                case 12:
                    return "D1";
                case 13:
                    return "D2";
                case 14:
                    return "D3";
                case 15:
                    return "D4";
                case 16:
                    return "E1";
                case 17:
                    return "E2";
                case 18:
                    return "E3";
                case 19:
                    return "E4";
                case 20:
                    return "F1";
                case 21:
                    return "F2";
                case 22:
                    return "F3";
                case 23:
                    return "F4";
                case 24:
                    return "G1";
                case 25:
                    return "G2";
                case 26:
                    return "G3";
                case 27:
                    return "G4";
                case 28:
                    return "H1";
                case 29:
                    return "H2";
                case 30:
                    return "H3";
                case 31:
                    return "H4";
            }

            return string.Empty;
        }

        public string GetValue(DateTime time)
        {
            double millisecondsFromStart = (time - this.Block.Start).TotalMilliseconds;
            double periods = millisecondsFromStart / this.RegPeriod;
            
            if (periods < this.Values.Count)
            {
                int x = this.Values[(int)periods];

                if (x > 32500)
                {
                    switch (x)
                    {
                        case 32501:
                            return "< min"; //
                        case 32502:
                            return "> max";
                        case 32503:
                            return "Break"; // Обрив 
                        case 32504:
                            return "Setup"; // Настройка Налаштування
                        case 32767:
                            return "Failure"; // Аварія
                    }
                }
                else
                {
                    float diapason = (this.Max - this.Min);
                    double value = Math.Round(((x - 3750) / 25000f) * diapason + this.Min, this.afterComma);
                    string s = value.ToString();

                    string comma = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;

                    var words = s.Split(comma[0]);
                    if (this.afterComma != 0)
                    {
                        if (words.Length == 1)
                        {
                            s += comma;
                            s = s.PadRight(s.Length + this.afterComma, '0');
                        }
                        else
                        {
                            s = s.PadRight(s.Length - words[1].Length + this.afterComma, '0');
                        }
                    }

                    return s;
                }
            }

            return "";
        }

        public int GetMax(DateTime start, DateTime end, DateTime intervalStart)
        {
            double millisecondsFromStart = (start - intervalStart).TotalMilliseconds;
            double startIndex = millisecondsFromStart / this.RegPeriod;
            double endIndex = (end - intervalStart).TotalMilliseconds / this.RegPeriod;
            int max = 0;
            for (int i = (int)startIndex; i < endIndex; i++)
            {
                if (this.Values[i] > max && this.Values[i] <= 32500)
                {
                    max = this.Values[i];
                }
            }

            if (max == 0)
            {
                max = 32501;
            }

            return max;
        }

        public int GetMin(DateTime start, DateTime end, DateTime intervalStart)
        {
            double millisecondsFromStart = (start - intervalStart).TotalMilliseconds;
            double startIndex = millisecondsFromStart / this.RegPeriod;
            double endIndex = (end - intervalStart).TotalMilliseconds / this.RegPeriod;
            int min = int.MaxValue;
            for (int i = (int)startIndex; i < endIndex; i++)
            {
                if (this.Values[i] < min)
                {
                    min = this.Values[i];
                }
            }

            return min;
        }

        public Channel CopyTime(DateTime start, DateTime end)
        {
            int millisecondsFromStart = (int)(start - this.Block.Start).TotalMilliseconds;
            int millisecondsBeforeEnd = (int)(this.Block.End - end).TotalMilliseconds;

            if (millisecondsFromStart < 0)
            {
                millisecondsFromStart = 0;
            }

            if (millisecondsBeforeEnd < 0)
            {
                millisecondsBeforeEnd = 0;
            }

            int endIndex = (int)((this.Block.End - this.Block.Start).TotalMilliseconds - millisecondsBeforeEnd)
                           / this.RegPeriod;;
            int startIndex = millisecondsFromStart / this.RegPeriod;

            if (((this.Block.End - this.Block.Start).TotalMilliseconds - millisecondsBeforeEnd) % this.RegPeriod != 0)
            {
                endIndex++;
            }

            if (start <= this.Block.Start)
            {
                return this.CopyFromStart(endIndex);
            }

            if (end >= this.Block.End)
            {
                return this.CopyToEnd(startIndex);
            }

            if (start <= this.Block.Start && end >= this.Block.End)
            {
                return this.Clone();
            }


            Channel channel = this.CopyProperties();
            
            for (int i = startIndex; i < endIndex; i++)
            {
                channel.Values.Add(this.Values[i]);
            }

            return channel;
        }

        private Channel Clone()
        {
            Channel channel = this.CopyProperties();

            channel.Values = new List<ushort>(this.Values.ToArray());

            return channel;
        }

        private Channel CopyToEnd(int startIndex)
        {
            Channel channel = this.CopyProperties();
            channel.Values = new List<ushort>();

            for (int i = startIndex; i < this.Values.Count; i++)
            {
                channel.Values.Add(this.Values[i]);
            }

            return channel;
        }

        private Channel CopyFromStart(int endIndex)
        {
            Channel channel = this.CopyProperties();
            channel.Values = new List<ushort>();

            for (int i = 0; i < endIndex; i++)
            {
                channel.Values.Add(this.Values[i]);
            }

            return channel;
        }

        private Channel CopyProperties()
        {
            Channel channel = new Channel(this.Block);
            channel.Id = this.Id;
            channel.Max = this.Max;
            channel.Min = this.Min;
            channel.IsActive = this.IsActive;
            channel.Name = this.Name;
            channel.RegPeriod = this.RegPeriod;
            channel.ValueUnits = this.ValueUnits;
            channel.sensorIndex = this.sensorIndex;
            channel.Color = this.Color;
            return channel;
        }
    }
}